﻿// //  ********************************************************************************
// //  Copyright (C) 2016 Sebastian Kenter. All rights reserved.
// // 
// //  Name    : PhotonServerProject | LoginServer | LoginServer.cs
// //  Created : 2016 - 03 - 24 16:01
// //  Modified: 2016 - 04 - 18 09:38
// //  ********************************************************************************

using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using ExitGames.Logging;
using PhotonServerCommon.Enums;
using PhotonServerFramework.Implementation.Application;
using PhotonServerFramework.Implementation.Configuration;
using PhotonServerFramework.Interfaces.Application;
using PhotonServerFramework.Interfaces.Client;
using PhotonServerFramework.Interfaces.Configuration;
using PhotonServerFramework.Interfaces.Handler;
using PhotonServerFramework.Interfaces.Server;

namespace LoginServer
{
    public class LoginServer : IServerApplication
    {
        private ServerApplication Server { get; }
        private IClientPeerRegistry ClientPeerRegistry { get; }
        private IServerPeerRegistry ServerPeerRegistry { get; }
        private IServerHandlerRegistry ServerHandlerRegistry { get; }
        private IClientHandlerRegistry ClientHandlerRegistry { get; }

        public ILogger Logger { get; }
        public IServerConfiguration Configuration { get; private set; }

        public Guid ServerId { get; }
        public ServerTypes ServerType => ServerTypes.MasterServer;
        private readonly IEnumerable<IClientTelegramHandler<IClientPeer>> _clientTelegramHandlers;
        private readonly IEnumerable<IServerTelegramHandler<IServerPeer>> _serverTelegramHandlers;

        private short _numReconnectTries;
        private short _isReconnecting;
        private Timer _reconnectTimer;

        public LoginServer(ILogger logger, ServerApplication server,
                           IClientPeerRegistry clientPeerRegistry,
                           IServerPeerRegistry serverPeerRegistry,
                           IEnumerable<IServerTelegramHandler<IServerPeer>> serverTelegramHandlers,
                           IEnumerable<IClientTelegramHandler<IClientPeer>> clientTelegramHandlers,
                           IServerHandlerRegistry serverHandlerRegistry,
                           IClientHandlerRegistry clientHandlerRegistry)
        {
            Logger = logger;
            Server = server;
            ServerId = Server.ServerId;
            ClientPeerRegistry = clientPeerRegistry;
            ServerPeerRegistry = serverPeerRegistry;
            _serverTelegramHandlers = serverTelegramHandlers;
            _clientTelegramHandlers = clientTelegramHandlers;
            ServerHandlerRegistry = serverHandlerRegistry;
            ClientHandlerRegistry = clientHandlerRegistry;
        }

        public void Setup()
        {
            Logger.InfoFormat("************************************************************************");
            Configuration = new ServerConfiguration(Server.ApplicationName, Logger);

            Logger.InfoFormat("************************************************************************");
            Logger.InfoFormat("** Starting up new server of type {0}", Server.ApplicationName);
            Logger.InfoFormat("** Server ID: {0}", ServerId.ToString());
            Logger.InfoFormat("** Master connections on IP: {0}:{1}", Configuration.IpAddress,
                Configuration.MasterTcpPort);
            Logger.InfoFormat("** Client connections on IP: {0}:{1}", Configuration.IpAddress,
                Configuration.ClientUdpPort);
            Logger.InfoFormat("************************************************************************");
            Logger.InfoFormat("Registering server handlers now");
            RegisterHandler(_serverTelegramHandlers);
            Logger.InfoFormat("************************************************************************");
            Logger.InfoFormat("Registering server handlers now");
            RegisterHandler(_clientTelegramHandlers);
            Logger.InfoFormat("************************************************************************");

            ConnectToMaster();
        }

        public void TearDown()
        {
            Logger.InfoFormat("************************************************************************");
            Logger.InfoFormat("** Tearing down server of type {0}", Server.ApplicationName);
            Logger.InfoFormat("** Server ID: {0}", ServerId.ToString());
            Logger.InfoFormat("** Master connections on IP: {0}:{1}", Configuration.IpAddress,
                Configuration.MasterTcpPort);
            Logger.InfoFormat("** Client connections on IP: {0}:{1}", Configuration.IpAddress,
                Configuration.ClientUdpPort);
            Logger.InfoFormat("************************************************************************");
            // Cleaning up connected servers and clients
        }

        public void OnStopRequested()
        {
            Logger.InfoFormat("************************************************************************");
            Logger.InfoFormat("** Stop requested for server of type {0}", Server.ApplicationName);
            Logger.InfoFormat("** Server ID: {0}", ServerId.ToString());
            Logger.InfoFormat("** Master connections on IP: {0}:{1}", Configuration.IpAddress,
                Configuration.MasterTcpPort);
            Logger.InfoFormat("** Client connections on IP: {0}:{1}", Configuration.IpAddress,
                Configuration.ClientUdpPort);
            Logger.InfoFormat("************************************************************************");

            // Cleaning up handlers

            Logger.InfoFormat("Executing clean up jobs before final shutdown of server {0}", Server.ApplicationName);
            Logger.InfoFormat("Unregistering server handlers from {0}", Server.ApplicationName);
            foreach (IServerTelegramHandler<IServerPeer> handler in _serverTelegramHandlers)
            {
                ServerHandlerRegistry.UnregisterServerHandler(handler.HandlerId);
            }
            Logger.InfoFormat("************************************************************************");
            Logger.InfoFormat("Unregistering client handlers from {0}", Server.ApplicationName);
            foreach (IClientTelegramHandler<IClientPeer> handler in _clientTelegramHandlers)
            {
                ClientHandlerRegistry.UnregisterClientHandler(handler.HandlerId);
            }
            Logger.InfoFormat("************************************************************************");
            Logger.InfoFormat("Unregistering server peers from {0}", Server.ApplicationName);
            ServerPeerRegistry.UnregisterServerPeers(ServerId);
            Logger.InfoFormat("************************************************************************");
            Logger.InfoFormat("Unregistering client peers from {0}", Server.ApplicationName);
            ClientPeerRegistry.UnregisterClientPeers(ServerId);
            Logger.InfoFormat("************************************************************************");
        }

        public void OnServerConnectionFailed(int errorCode, string errorMessage, object state)
        {
            IPEndPoint masterEndPoint = new IPEndPoint(IPAddress.Parse(Configuration.MasterIpAddress),
                Configuration.MasterTcpPort);

            Logger.WarnFormat("Master connection failed at {0}, errorCode={1}, message={2}", masterEndPoint, errorCode,
                errorMessage);

            ReconnectToMaster();
        }

        private void ConnectToMaster()
        {
            IPEndPoint masterEndPoint = new IPEndPoint(IPAddress.Parse(Configuration.MasterIpAddress),
                Configuration.MasterTcpPort);

            if (Server.ConnectToServerTcp(masterEndPoint, "MasterServer", masterEndPoint))
            {
                Logger.InfoFormat("Connecting to master server at {0}, server-id={1}", masterEndPoint, ServerId);
            }
            else
            {
                Logger.WarnFormat("MasterServer refused connection at {0} for server-id={1}", masterEndPoint, ServerId);
            }
        }

        private void ReconnectToMaster()
        {
            if (_numReconnectTries <= Configuration.MaximumRetries)
            {
                _numReconnectTries += 1;

                Logger.InfoFormat("Trying to reconnect to master server (interval={0}ms, try={1}, maxtries={2}",
                    Configuration.ConnectionRetryInterval, _numReconnectTries, Configuration.MaximumRetries);

                Thread.VolatileWrite(ref _isReconnecting, 1);

                _reconnectTimer = new Timer(o => ConnectToMaster(), null, Configuration.ConnectionRetryInterval*1000,
                    Timeout.Infinite);
            }
            else
            {
                Logger.ErrorFormat("No more retries available. Aborting connection retries.");
                _reconnectTimer.Dispose();
            }
        }

        private void RegisterHandler(IEnumerable<IServerTelegramHandler<IServerPeer>> serverTelegramHandlers)
        {
            foreach (IServerTelegramHandler<IServerPeer> handler in serverTelegramHandlers)
            {
                ServerHandlerRegistry.RegisterServerHandler(handler, this);
            }
        }

        private void RegisterHandler(IEnumerable<IClientTelegramHandler<IClientPeer>> clientTelegramHandlers)
        {
            foreach (IClientTelegramHandler<IClientPeer> handler in clientTelegramHandlers)
            {
                ClientHandlerRegistry.RegisterClientHandler(handler, this);
            }
        }
    }
}
